home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 November: Tool Chest / Dev.CD Nov 94.toast / Sample Code / Macintosh Sample Code / SC.011.GetZoneList / GetZoneList.p < prev    next >
Encoding:
Text File  |  1992-08-10  |  22.2 KB  |  816 lines  |  [TEXT/MPS ]

  1. {------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    AppleTalk GetZoneList Sample Application
  6. #
  7. #    GetZoneList
  8. #
  9. #    GetZoneList.p    -    Pascal Source
  10. #
  11. #    Copyright © 1988-90 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    1.00                November 1988
  15. #                1.01                October 1989
  16. #                1.02                May 1990
  17. #                1.03                June 1992
  18. #                1.04                July 1992
  19. #
  20. #    Components:    GetZoneList.c        May 1, 1990
  21. #                GetZoneList.p        May 1, 1990
  22. #                GetZoneList.r        May 1, 1990
  23. #                MakeFile            May 1, 1990
  24. #                UFailure.a            November 1, 1988
  25. #                UFailure.h            November 1, 1988
  26. #                UFailure.inc1.p        November 1, 1988
  27. #                UFailure.p            November 1, 1988
  28. #
  29. #    GetZoneList is a sample application that uses
  30. #    AppleTalk ATP and ZIP to obtain a list of zones
  31. #    on an AppleTalk internet.
  32. #
  33. #    GetZoneList also demonstrates using a signal, or
  34. #    failure-catching mechanism to recover from error
  35. #    situations.
  36. #
  37. #    GetZoneList is based on DTS Sample.p. For more
  38. #    description and explanantion of the non-example
  39. #    specific areas of this application, please refer to
  40. #    either Sample.p or TESample.p.
  41. #
  42. ------------------------------------------------------------------------------}
  43.  
  44. PROGRAM GetZoneList;
  45.  
  46. USES
  47.     MemTypes, QuickDraw, OSIntf, ToolIntf, AppleTalk, PackIntf, FixMath, Script, UFailure;
  48.  
  49. CONST
  50.     _WaitNextEvent            = $A860;
  51.     _Unimplemented            = $A89F;
  52.     kSysEnvironsVersion        = 1;
  53.     kOSEvent                = app4Evt;    {event used by MultiFinder}
  54.     kSuspendResumeMessage    = 1;        {high byte of suspend/resume event message}
  55.     kResumeMask                = 1;        {bit of message field for resume vs. suspend}
  56.  
  57.     kCR                = 13;                {carriage return character}
  58.     kENTER            = 3;                {enter character}
  59.     kScrollBarWidth    = 15;                {the width of the scrollbar in the list}
  60.     kListInset        = -1;                {adjustment for list frame}
  61.     kATPTimeOutVal    = 3;                {re-try ATP SendRequest every 3 seconds}
  62.     kATPRetryCount    = 5;                {for five times}
  63.     kZonesSize        = 578;                {size of buffer for zone names}
  64.     kGZLCall        = $08000000;        {GetZoneList indicator}
  65.     kZIPSocket        = 6;                {the Zone Information Protocol socket}
  66.     kMoreZones        = $FF000000;        {mask to see if more zones to come}
  67.     kZoneCount        = $0000FFFF;        {mask to count zones in buffer}
  68.     kHilite            = 1;                {hilite value for button control}
  69.     kDeHilite        = 0;                {dehilite value for button control}
  70.     kHiliteDelay    = 8;                {time in ticks to leave button hilited}
  71.  
  72.     kMinHeap        = (29 * 1024);
  73.     kMinSpace        = (20 * 1024);
  74.  
  75.     sErrStrings        = 128;                {error string STR#}
  76.     eStandardErr    = 1;
  77.     eWrongMachine    = 2;
  78.     eSmallSize        = 3;
  79.     eNoMemory        = 4;
  80.     eAppleTalk        = 5;
  81.     eNoZones        = 6;
  82.  
  83.     rAboutAlert        = 128;                {about alert}
  84.     rZoneDialog        = 129;                {zone list dialog}
  85.     dZoneList        = 2;                {user item that is zone list}
  86.     dDefault        = 3;                {user item that is default indicator}
  87.     rUserAlert        = 130;                {error alert}
  88.  
  89.     rMenuBar        = 128;                {application's menu bar}
  90.  
  91.     mApple            = 128;                {Apple menu}
  92.     iAbout            = 1;
  93.  
  94.     mFile            = 129;                {File menu}
  95.     iNew            = 1;
  96.     iClose            = 4;
  97.     iQuit            = 12;
  98.  
  99.     mEdit            = 130;                {Edit menu}
  100.     iUndo            = 1;
  101.     iCut            = 3;
  102.     iCopy            = 4;
  103.     iPaste            = 5;
  104.     iClear            = 6;
  105.  
  106.     {1.01 - kDITop and kDILeft are used to locate the Disk Initialization dialogs.}
  107.     kDITop        = $0050;
  108.     kDILeft        = $0070;
  109.  
  110. VAR
  111.     gMac                : SysEnvRec;    {set up by Initialize}
  112.     gHasWaitNextEvent    : BOOLEAN;        {set up by Initialize}
  113.     gInBackground        : BOOLEAN;        {maintained by Initialize and DoEvent}
  114.  
  115.     gList                : ListHandle;    {the list to be filled with zone names}
  116.  
  117.  
  118.  
  119. {$S Initialize}
  120. FUNCTION TrapAvailable(tNumber: INTEGER; tType: TrapType): BOOLEAN;
  121.  
  122. {Check to see if a given trap is implemented. This is only used by the
  123.  Initialize routine in this program, so we put it in the Initialize segment.
  124.  The recommended approach to see if a trap is implemented is to see if
  125.  the address of the trap routine is the same as the address of the
  126.  Unimplemented trap.}
  127. {1.02 - Needs to be called after call to SysEnvirons so that it can check
  128.  if a ToolTrap is out of range of a pre-MacII ROM.}
  129.  
  130. BEGIN
  131.     IF (tType = ToolTrap) &
  132.         (gMac.machineType > envMachUnknown) &
  133.         (gMac.machineType < envMacII) THEN BEGIN        {it's a 512KE, Plus, or SE}
  134.         tNumber := BAND(tNumber, $03FF);
  135.         IF tNumber > $01FF THEN                            {which means the tool traps}
  136.             tNumber := _Unimplemented;                    {only go to $01FF}
  137.     END;
  138.     TrapAvailable := NGetTrapAddress(tNumber, tType) <>
  139.                         GetTrapAddress(_Unimplemented);
  140. END; {TrapAvailable}
  141.  
  142.  
  143. {$S Main}
  144. PROCEDURE FailOSErrMsg(result, message: INTEGER);
  145. BEGIN
  146.     IF result <> noErr THEN
  147.         Failure(result, message);
  148. END; {SignalOSErrMsg}
  149.  
  150.  
  151. {$S Main}
  152. PROCEDURE FailNILMsg(p: UNIV Ptr; message: INTEGER);
  153. BEGIN
  154.     IF p = NIL THEN
  155.         Failure(memFullErr, message);
  156. END; {FailNILMsg}
  157.  
  158.  
  159. {$S Main}
  160. PROCEDURE AlertUser(error: INTEGER; message: LongInt);
  161.  
  162. {Display an alert to inform the user of an error. Message acts as an
  163.  index into a STR# resource of error messages. If no message is given,
  164.  i.e. = 0, then use a standard message. If error is not noErr then
  165.  display it as well.}
  166.  
  167. VAR
  168.     msg1, msg2    : Str255;
  169.     itemHit        : INTEGER;
  170. BEGIN
  171.     IF message <= 0 THEN message := eStandardErr;
  172.     GetIndString(msg1, sErrStrings, message);
  173.     IF error = noErr THEN
  174.         msg2 := ''
  175.     ELSE
  176.         NumToString(error, msg2);
  177.     ParamText(msg1, msg2, '', '');
  178.     itemHit := Alert(rUserAlert, NIL);
  179. END; {AlertUser}
  180.  
  181.  
  182. {$S Main}
  183. FUNCTION IsDAWindow(window: WindowPtr): BOOLEAN;
  184. BEGIN
  185.     IF window = NIL THEN
  186.         IsDAWindow := FALSE
  187.     ELSE    {DA windows have negative windowKinds}
  188.         IsDAWindow := WindowPeek(window)^.windowKind < 0;
  189. END; {IsDAWindow}
  190.  
  191.  
  192. {$S Main}
  193. FUNCTION IsAppWindow(window: WindowPtr): BOOLEAN;
  194. BEGIN
  195.     IF window = NIL THEN
  196.         IsAppWindow := FALSE
  197.     ELSE    {application windows have windowKinds >= userKind (8) or dialogKind (2)}
  198.         WITH WindowPeek(window)^ DO
  199.             IsAppWindow := (windowKind >= userKind) | (windowKind = dialogKind);
  200. END; {IsAppWindow}
  201.  
  202.  
  203. {$S Main}
  204. PROCEDURE BuildZoneList;
  205.  
  206. {Create the list of zones on the network. Find a bridge to talk to , if one is
  207.  present, then ask it for zone names. Add the names to the list in the dialog.}
  208.  
  209. VAR
  210.     dATPPBptr                    : ATPPBptr;        {the parameter block for GetZoneList call}
  211.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  212.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  213.     dIndex, dCount, dNode, dNet    : INTEGER;
  214.     ignore                        : INTEGER;
  215.     cSize                        : Point;
  216.     fi                            : FailInfo;
  217.     nodeNetAddress, bridgeNode    : INTEGER;
  218.  
  219.     PROCEDURE CleanUp;
  220.     BEGIN
  221.         IF dATPPBptr <> NIL THEN
  222.             DisposPtr(Ptr(dATPPBptr));                    {get rid of pb block}
  223.         IF dZones <> NIL THEN
  224.             DisposPtr(dZones);                            {and buffer}
  225.     END; {CleanUp}
  226.  
  227.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  228.     BEGIN
  229.         CleanUp;                                        {get rid of allocated junk}
  230.     END;
  231.  
  232. BEGIN
  233.     dATPPBptr := NIL;                                    {init some important variables}
  234.     dZones := NIL;
  235.     CatchFailures(fi, HandleErr);
  236.  
  237.     { get network address of node & node ID of bridge (if any) }
  238.     FailOSErrMsg(GetNodeAddress(ignore, nodeNetAddress), eAppleTalk);
  239.     bridgeNode := GetBridgeAddress;
  240.  
  241.     { test to see if bridge node fails.  If so, no internet. }
  242.     if (bridgeNode = 0) then
  243.         Failure(0, eNoZones);                                    { bail if no zones present }
  244.  
  245.     dATPPBptr := ATPPBptr(NewPtr(SIZEOF(ATPParamBlock)));
  246.     FailNILMsg(dATPPBptr, eNoMemory);
  247.     dZones := NewPtr(kZonesSize);
  248.     FailNILMsg(dZones, eNoMemory);
  249.     WITH dBDS DO BEGIN                                    {set up BDS}
  250.         buffSize := kZonesSize;
  251.         buffPtr := dZones;
  252.         END;
  253.     WITH dATPPBptr^ DO BEGIN                            {set up pb block}
  254.         atpFlags := 0;
  255.  
  256.         addrBlock.aNet := nodeNetAddress;
  257.         addrBlock.aNode := bridgeNode;                    {get node of bridge}
  258.         addrBlock.aSocket := kZIPSocket;                {the socket we want}
  259.         reqLength := 0;
  260.         reqPointer := NIL;
  261.         bdsPointer := @dBDS;
  262.         numOfBuffs := 1;
  263.         timeOutVal := kATPTimeOutVal;
  264.         retryCount := kATPRetryCount;
  265.         END;
  266.     dIndex := 1;
  267.     dCount := 0;
  268.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  269.     REPEAT
  270.         dATPPBptr^.userData := kGZLCall + dIndex;        {indicate GetZoneList request}
  271.         FailOSErrMsg(PSendRequest(dATPPBptr,
  272.             FALSE), eAppleTalk);                        {send sync request}
  273.         dCount := dCount + BAND(dBDS.userBytes,
  274.                     kZoneCount);                        {find out how many returned}
  275.         dCurr := dZones;                                {put current pointer at start}
  276.         REPEAT                                            {get each zone}
  277.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  278.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  279.                         cSize, gList);                    {stuff in zone}
  280.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  281.             dIndex := dIndex + 1;                        {increment which zone}
  282.         UNTIL dIndex > dCount;
  283.     UNTIL (BAND(dBDS.userBytes, kMoreZones) <> 0);        {keep going until none left}
  284.     CleanUp;
  285.  
  286.     Success(fi);
  287. END; {BuildZoneList}
  288.  
  289.  
  290. {$S Main}
  291. PROCEDURE BuildZoneListPhase2;
  292.  
  293. {Create the list of zones on the network. Find a bridge to talk to , if one is
  294.  present, then ask it for zone names. Add the names to the list in the dialog.}
  295.  
  296. VAR
  297.     dXPBPBPtr                    : xPPParmBlkPtr;
  298.     dBDS                        : BDSElement;    {the BDS for GetZoneList call}
  299.     dZones, dCurr                : Ptr;            {the data buffer for GetZoneList call}
  300.     dIndex, dCount, dNode, dNet    : INTEGER;
  301.     ignore                        : INTEGER;
  302.     cSize                        : Point;
  303.     fi                            : FailInfo;
  304.     xppDriverRefNum                : INTEGER;
  305.  
  306.     PROCEDURE CleanUp;
  307.     BEGIN
  308.         IF dXPBPBPtr <> NIL THEN
  309.             DisposPtr(Ptr(dXPBPBPtr));                    {get rid of pb block}
  310.         IF dZones <> NIL THEN
  311.             DisposPtr(dZones);                            {and buffer}
  312.     END; {CleanUp}
  313.  
  314.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  315.     BEGIN
  316.         CleanUp;                                        {get rid of allocated junk}
  317.     END;
  318.  
  319. BEGIN
  320.     dXPBPBPtr := NIL;                                    {init some important variables}
  321.     dZones := NIL;
  322.     CatchFailures(fi, HandleErr);
  323.  
  324.     { Get network address of bridge.  If zero, no internet. }
  325.     if (GetBridgeAddress = 0) then
  326.         Failure(0, eNoZones);                                    { bail if no zones present }
  327.  
  328.     { get a hold of the XPP driver reference number-this is the safest way }
  329.     FailOSErrMsg(OpenDriver('.XPP', xppDriverRefNum), eAppleTalk);
  330.  
  331.     dXPBPBPtr := xPPParmBlkPtr(NewPtr(SIZEOF(xPPParamBlock)));
  332.     FailNILMsg(dXPBPBPtr, eNoMemory);
  333.     dZones := NewPtr(kZonesSize);
  334.     FailNILMsg(dZones, eNoMemory);
  335.  
  336.     dXPBPBPtr^.zipInfoField[1] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  337.     dXPBPBPtr^.zipInfoField[2] := 0;    { ALWAYS 0 on first call.  has state info on subsequent calls }
  338.     dXPBPBPtr^.zipLastFlag := 0;
  339.  
  340.     dXPBPBPtr^.ioRefNum := xppDriverRefNum;
  341.     dXPBPBPtr^.csCode := xCall;
  342.     dXPBPBPtr^.xppSubCode := zipGetZoneList;
  343.     dXPBPBPtr^.xppTimeOut := kATPTimeOutVal;
  344.     dXPBPBPtr^.xppRetry := kATPRetryCount;
  345.     dXPBPBPtr^.zipBuffPtr := Ptr( dZones);
  346.  
  347.     dIndex := 1;
  348.     dCount := 0;
  349.     SetPt(cSize, 0, 0);                                    {we always stuff into first}
  350.     REPEAT
  351.         FailOSErrMsg(PBControl(ParmBlkPtr (dXPBPBPtr), false), eAppleTalk);        { send sync control call }
  352.         dCount := dCount + dXPBPBPtr^.zipNumZones;                        { find out how many returned }
  353.  
  354.         dCurr := dZones;                                {put current pointer at start}
  355.         REPEAT                                            {get each zone}
  356.             ignore := LAddRow(1, 0, gList);                {create new cell at start}
  357.             LSetCell(POINTER(ORD4(dCurr) + 1), dCurr^,
  358.                 cSize, gList);                            {stuff in zone}
  359.             dCurr := POINTER(ORD4(dCurr) + dCurr^+1);    {bump up current pointer}
  360.             dIndex := dIndex + 1;                        {increment which zone}
  361.         UNTIL dIndex > dCount;
  362.     UNTIL (dXPBPBPtr^.zipLastFlag <> 0);        {keep going until none left}
  363.     CleanUp;
  364.  
  365.     Success(fi);
  366. END; {BuildZoneListPhase2}
  367.  
  368.  
  369. {$S Main}
  370. PROCEDURE ZoneListDraw(dlg: DialogPtr; item: INTEGER);
  371.  
  372. {The user item procedure for the zone list user item and default
  373.  box user item in the dialog. Draw the list and the frame that goes with it.
  374.  Draw the default box around the OK button.}
  375.  
  376. VAR
  377.     port    : GrafPtr;
  378.     kind    : INTEGER;
  379.     h        : Handle;
  380.     r        : Rect;
  381.     ps        : PenState;
  382.  
  383. BEGIN
  384.     GetPort(port);                                    {save old port}
  385.     SetPort(dlg);                                    {make dialog port}
  386.     CASE item OF
  387.     dZoneList: BEGIN
  388.             LUpdate(dlg^.visRgn, gList);            {re-draw list}
  389.             GetDItem(dlg, dZoneList, kind, h, r);
  390.             InsetRect(r, kListInset, kListInset);
  391.             FrameRect(r);                            {re-draw frame}
  392.         END;
  393.     dDefault: BEGIN
  394.             GetDItem(dlg, dDefault, kind, h, r);
  395.             GetPenState(ps);
  396.             PenNormal;                                {always be on the defensive}
  397.             PenSize(3, 3);
  398.             InsetRect(r, -4, -4);
  399.             FrameRoundRect(r, 16, 16);                {draw default box}
  400.             SetPenState(ps);
  401.         END;
  402.     END;
  403.     SetPort(port);                                    {restore old port}
  404. END; {ZoneListDraw}
  405.  
  406.  
  407. {$S Main}
  408. FUNCTION ListFilter (dlg: DialogPtr; VAR event: EventRecord;
  409.                                             VAR item: INTEGER) : BOOLEAN;
  410.  
  411. {Passed as parameter to ModalDialog. Handle key presses and mouse clicks
  412.  from the user. Do all the right default actions since we override them
  413.  by virtue of our existence.}
  414.  
  415. VAR
  416.     port        : GrafPtr;
  417.     loc            : Point;
  418.     kind        : INTEGER;
  419.     h            : Handle;
  420.     r            : Rect;
  421.     ignore        : BOOLEAN;
  422.     key            : SignedByte;
  423.     finalTicks    : LongInt;
  424. BEGIN
  425.     ListFilter := FALSE;                                {always default FALSE}
  426.     CASE event.what OF
  427.         keyDown, autoKey: BEGIN                            {check for <cr> or <enter>}
  428.             key := SignedByte(event.message);
  429.             IF key IN [kCR, kENTER] THEN BEGIN            {it was a <cr> or <enter>}
  430.                 GetDItem(dlg, ok, kind, h, r);
  431.                 HiliteControl(ControlHandle(h), kHilite);
  432.                 Delay(kHiliteDelay, finalTicks);
  433.                 HiliteControl(ControlHandle(h), kDeHilite);
  434.                 ListFilter := TRUE;                        {so we handle it}
  435.                 item := 1;                                {and make the first item hit}
  436.             END;
  437.         END;
  438.         mouseDown: BEGIN                                {we want mouseDowns}
  439.             GetPort(port);
  440.             SetPort(dlg);
  441.             loc := event.where;
  442.             GlobalToLocal(loc);                            {find where clicked}
  443.             GetDItem(dlg, dZoneList, kind, h, r);        {get rect for list}
  444.             IF PtInRect(loc, r) THEN BEGIN                {if clicked inside…}
  445.                 ListFilter := TRUE;                        {we take care of it}
  446.                 ignore := LClick(loc, event.modifiers,
  447.                                     gList);                {by passing click to list}
  448.             END;
  449.             SetPort(port);
  450.         END;
  451.     END;
  452. END; {ListFilter}
  453.  
  454.  
  455. {$S Main}
  456. PROCEDURE DoZoneList;
  457.  
  458. {Put up a modal dialog that shows a list of the zones on the net. Create the dialog
  459.  and list, call BuildZoneList to fill it, then wait for the user to click OK.}
  460.  
  461. VAR
  462.     dlg                        : DialogPtr;
  463.     item, kind                : INTEGER;
  464.     h                        : Handle;
  465.     r, rView, dataBounds    : Rect;
  466.     cSize                    : Point;
  467.     fi                        : FailInfo;
  468.     hor, ver                : INTEGER;
  469.  
  470.     PROCEDURE CleanUp;
  471.     BEGIN
  472.         IF gList <> NIL THEN
  473.             LDispose(gList);                                {get rid of list}
  474.         IF dlg <> NIL THEN
  475.             DisposDialog(dlg);                                {get rid of dialog}
  476.     END; {CleanUp}
  477.  
  478.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  479.     BEGIN
  480.         CleanUp;                                            {release junk}
  481.     END;
  482.  
  483. BEGIN
  484.     gList := NIL;                                            {init some important variables}
  485.     dlg := NIL;
  486.     CatchFailures(fi, HandleErr);
  487.  
  488.     dlg := GetNewDialog(rZoneDialog, NIL, POINTER(-1));        {create dialog}
  489.     FailNILMsg(dlg, eNoMemory);
  490.  
  491.     {We center the dialog horizontally and position it vertically one-third the
  492.      distance from the menu bar to the bottom of the main device. We do not
  493.      check for the dialog extending past the bottom of the device because we
  494.      know the dialog is not that big. You may wish to make that check.}
  495.     WITH dlg^.portRect DO BEGIN
  496.         hor := right - left;
  497.         ver := bottom - top;
  498.     END;
  499.     WITH screenBits.bounds DO BEGIN
  500.         hor := ((right - left) - hor) DIV 2;
  501.         ver := (((bottom - top) - ver - GetMBarHeight) DIV 3) + GetMBarHeight;
  502.     END;
  503.     MoveWindow(dlg, hor, ver, FALSE);
  504.  
  505.     GetDItem(dlg, dDefault, kind, h, r);
  506.     h := @ZoneListDraw;                                        {connect drawing procedure}
  507.     SetDItem(dlg, dDefault, kind, h, r);
  508.     GetDItem(dlg, dZoneList, kind, h, r);
  509.     h := @ZoneListDraw;                                        {connect drawing procedure}
  510.     SetDItem(dlg, dZoneList, kind, h, r);
  511.     rView := r;
  512.     WITH rView DO
  513.         right := right - kScrollBarWidth;                    {adjust rectangle for scroll}
  514.     SetRect(dataBounds, 0, 0, 1, 0);                        {init to one-wide list}
  515.     SetPt(cSize, 0, 0);
  516.     gList := LNew(rView, dataBounds, cSize, 0, WindowPtr(dlg),
  517.                     FALSE, FALSE, FALSE, TRUE);                {create with vertical scroll}
  518.     FailNILMsg(gList, eNoMemory);
  519.  
  520.     if gMac.atDrvrVersNum >= 53 then
  521.         BuildZoneListPhase2
  522.     else
  523.         BuildZoneList;                                            {put the stuff into the list}
  524.  
  525.     SetPt(cSize, 0, 0);
  526.     LSetSelect(TRUE, cSize, gList);                            {select the first guy}
  527.     LDoDraw(TRUE, gList);                                    {turn on the list}
  528.     ShowWindow(dlg);                                        {turn on the dialog}
  529.     REPEAT
  530.         ModalDialog(@ListFilter, item);                        {accept events}
  531.     UNTIL item = ok;                                        {until he presses 'ok'}
  532.     CleanUp;
  533.  
  534.     Success(fi);
  535. END; {DoZoneList}
  536.  
  537.  
  538. {$S Main}
  539. FUNCTION DoCloseWindow(window: WindowPtr) : BOOLEAN;
  540. BEGIN
  541.     DoCloseWindow := TRUE;
  542.     IF IsDAWindow(window) THEN
  543.         CloseDeskAcc(WindowPeek(window)^.windowKind);
  544.     IF IsAppWindow(window) THEN
  545.         CloseWindow(window);
  546. END; {DoCloseWindow}
  547.  
  548.  
  549. {$S Initialize}
  550. PROCEDURE Initialize;
  551. VAR
  552.     menuBar            : Handle;
  553.     window            : WindowPtr;
  554.     ignoreError        : OSErr;
  555.     total, contig    : LongInt;
  556.     ignoreResult    : BOOLEAN;
  557.     event            : EventRecord;
  558.     count            : INTEGER;
  559.     fi                : FailInfo;
  560.  
  561.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  562.     BEGIN
  563.         IF error > 0 THEN
  564.             AlertUser(0, error)
  565.         ELSE
  566.             AlertUser(error, message);
  567.         ExitToShell;
  568.     END; {HandleErr}
  569.  
  570. BEGIN
  571.     gHasWaitNextEvent := TrapAvailable(_WaitNextEvent, ToolTrap);
  572.     gInBackground := FALSE;
  573.  
  574.     InitGraf(@thePort);
  575.     InitFonts;
  576.     InitWindows;
  577.     InitMenus;
  578.     TEInit;
  579.     InitDialogs(NIL);
  580.     InitCursor;
  581.  
  582.     FOR count := 1 TO 3 DO
  583.         ignoreResult := EventAvail(everyEvent, event);
  584.  
  585.     CatchFailures(fi, HandleErr);
  586.  
  587.     FailOSErrMsg(MPPOpen, eAppleTalk);
  588.     FailOSErrMsg(ATPLoad, eAppleTalk);
  589.  
  590.     ignoreError := SysEnvirons(kSysEnvironsVersion, gMac);
  591.     IF gMac.machineType < 0 THEN
  592.         Failure(0, eWrongMachine);
  593.  
  594.     IF ORD(GetApplLimit) - ORD(ApplicZone) < kMinHeap THEN
  595.         Failure(0, eSmallSize);
  596.  
  597.     PurgeSpace(total, contig);
  598.     IF total < kMinSpace THEN
  599.         Failure(0, eNoMemory);
  600.  
  601.     menuBar := GetNewMBar(rMenuBar);        {read menus into menu bar}
  602.     FailNILMsg(menuBar, eNoMemory);
  603.     SetMenuBar(menuBar);                    {install menus}
  604.     DisposHandle(menuBar);
  605.     AddResMenu(GetMHandle(mApple), 'DRVR');    {add DA names to Apple menu}
  606.     DrawMenuBar;
  607.  
  608.     Success(fi);
  609. END; {Initialize}
  610.  
  611.  
  612. {$S Main}
  613. PROCEDURE Terminate;
  614. VAR
  615.     aWindow    : WindowPtr;
  616.     closed    : BOOLEAN;
  617.  
  618. BEGIN
  619.     closed := TRUE;
  620.     REPEAT
  621.         aWindow := FrontWindow;                    {get the current front window}
  622.         IF aWindow <> NIL THEN
  623.             closed := DoCloseWindow(aWindow);    {close this window}
  624.     UNTIL (NOT closed) | (aWindow = NIL);        {do all windows}
  625.     IF closed THEN
  626.         ExitToShell;                            {exit if no cancellation}
  627. END; {Terminate}
  628.  
  629.  
  630. {$S Main}
  631. PROCEDURE AdjustMenus;
  632. VAR
  633.     window            : WindowPtr;
  634.     menu            : MenuHandle;
  635.  
  636. BEGIN
  637.     window := FrontWindow;
  638.  
  639.     menu := GetMHandle(mFile);
  640.     IF IsDAWindow(window) THEN                {we can allow desk accessories to be closed from the menu}
  641.         EnableItem(menu, iClose)
  642.     ELSE
  643.         DisableItem(menu, iClose);            {but not our traffic light window}
  644.  
  645.     menu := GetMHandle(mEdit);
  646.     IF IsDAWindow(window) THEN BEGIN        {a desk accessory might need the edit menu}
  647.         EnableItem(menu, iUndo);
  648.         EnableItem(menu, iCut);
  649.         EnableItem(menu, iCopy);
  650.         EnableItem(menu, iPaste);
  651.         EnableItem(menu, iClear);
  652.     END ELSE BEGIN                            {but we know we do not}
  653.         DisableItem(menu, iUndo);
  654.         DisableItem(menu, iCut);
  655.         DisableItem(menu, iCopy);
  656.         DisableItem(menu, iClear);
  657.         DisableItem(menu, iPaste);
  658.     END;
  659.  
  660. END; {AdjustMenus}
  661.  
  662.  
  663. {$S Main}
  664. PROCEDURE DoMenuCommand(menuResult: LONGINT);
  665. VAR
  666.     menuID            : INTEGER;        {the resource ID of the selected menu}
  667.     menuItem        : INTEGER;        {the item number of the selected menu}
  668.     itemHit            : INTEGER;
  669.     daName            : Str255;
  670.     daRefNum        : INTEGER;
  671.     handledByDA        : BOOLEAN;
  672.     ignore            : BOOLEAN;
  673.     fi                : FailInfo;
  674.  
  675.     PROCEDURE HandleMenu(error: INTEGER; message: LongInt);
  676.     BEGIN
  677.         HiliteMenu(0);                {unhighlight what MenuSelect (or MenuKey) hilited}
  678.     END;
  679.  
  680. BEGIN
  681.     CatchFailures(fi, HandleMenu);
  682.     menuID := HiWrd(menuResult);    {use built-ins (for efficiency)...}
  683.     menuItem := LoWrd(menuResult);    {to get menu item number and menu number}
  684.     CASE menuID OF
  685.         mApple:
  686.             CASE menuItem OF
  687.                 iAbout:                {bring up alert for About}
  688.                     itemHit := Alert(rAboutAlert, NIL);
  689.                 OTHERWISE BEGIN        {all non-About items in this menu are DAs}
  690.                     GetItem(GetMHandle(mApple), menuItem, daName);
  691.                     daRefNum := OpenDeskAcc(daName);
  692.                 END;
  693.             END;
  694.         mFile:
  695.             CASE menuItem OF
  696.                 iNew:
  697.                     DoZoneList;
  698.                 iClose:
  699.                     ignore := DoCloseWindow(FrontWindow);
  700.                 iQuit:
  701.                     Terminate;
  702.             END;
  703.         mEdit:                        {call SystemEdit for DA editing & MultiFinder}
  704.             handledByDA := SystemEdit(menuItem-1);    {since we don't do any editing}
  705.     END;
  706.     Success(fi);
  707.     HiliteMenu(0);                    {cleanup}
  708. END; {DoMenuCommand}
  709.  
  710.  
  711. {$S Main}
  712. PROCEDURE DoEvent(event: EventRecord);
  713. VAR
  714.     part, err    : INTEGER;
  715.     window        : WindowPtr;
  716.     hit            : BOOLEAN;
  717.     key            : CHAR;
  718.     fi            : FailInfo;
  719.     aPoint        : Point;
  720.  
  721.     PROCEDURE HandleErr(error: INTEGER; message: LongInt);
  722.     BEGIN
  723.         IF error > 0 THEN
  724.             AlertUser(0, error)
  725.         ELSE
  726.             AlertUser(error, message);
  727.         EXIT(DoEvent);
  728.     END; {HandleErr}
  729.  
  730. BEGIN
  731.     CatchFailures(fi, HandleErr);
  732.  
  733.     CASE event.what OF
  734.         mouseDown: BEGIN
  735.             part := FindWindow(event.where, window);
  736.             CASE part OF
  737.                 inMenuBar: BEGIN            {process the menu command}
  738.                     AdjustMenus;
  739.                     DoMenuCommand(MenuSelect(event.where));
  740.                 END;
  741.                 inSysWindow:                {let the system handle the mouseDown}
  742.                     SystemClick(event, window);
  743.                 inContent:;
  744.                 inDrag:;
  745.                 inGrow:;
  746.                 inZoomIn, inZoomOut:;
  747.             END;
  748.         END;
  749.         keyDown, autoKey: BEGIN                {check for menukey equivalents}
  750.             key := CHR(BAnd(event.message, charCodeMask));
  751.             IF BAnd(event.modifiers, cmdKey) <> 0 THEN    {Command key down}
  752.                 IF event.what = keyDown THEN BEGIN
  753.                     AdjustMenus;            {enable/disable/check menu items properly}
  754.                     DoMenuCommand(MenuKey(key));
  755.                 END;
  756.         END;                                {call DoActivate with the window and...}
  757.         activateEvt:;
  758.         updateEvt:;
  759.         {1.01 - It is not a bad idea to at least call DIBadMount in response
  760.          to a diskEvt, so that the user can format a floppy.}
  761.         diskEvt:
  762.             IF HiWrd(event.message) <> noErr THEN BEGIN
  763.                 SetPt(aPoint, kDILeft, kDITop);
  764.                 err := DIBadMount(aPoint, event.message);
  765.             END;
  766.         kOSEvent:
  767.             CASE BAnd(BRotL(event.message, 8), 255) OF    {high byte of message}
  768.                 kSuspendResumeMessage: BEGIN
  769.                     gInBackground := BAnd(event.message, kResumeMask) = 0;
  770.                 END;
  771.             END;
  772.     END;
  773.  
  774.     Success(fi);
  775. END; {DoEvent}
  776.  
  777.  
  778. {$S Main}
  779. PROCEDURE EventLoop;
  780. VAR
  781.     cursorRgn    : RgnHandle;
  782.     gotEvent    : BOOLEAN;
  783.     event        : EventRecord;
  784.  
  785. BEGIN
  786.     cursorRgn := NewRgn;            {we’ll pass WNE an empty region the 1st time thru}
  787.  
  788.     REPEAT
  789.         IF gHasWaitNextEvent THEN    {put us 'asleep' forever under MultiFinder}
  790.             gotEvent := WaitNextEvent(everyEvent, event, MAXLONGINT, cursorRgn)
  791.         ELSE BEGIN
  792.             SystemTask;                {must be called if using GetNextEvent}
  793.             gotEvent := GetNextEvent(everyEvent, event);
  794.         END;
  795.         IF gotEvent THEN BEGIN
  796.             DoEvent(event);
  797.         END;
  798.     UNTIL FALSE;                    {loop forever; we quit through an ExitToShell}
  799. END; {EventLoop}
  800.  
  801.  
  802. PROCEDURE _DataInit; EXTERNAL;
  803.  
  804.  
  805. {$S Main}
  806. BEGIN
  807.     UnloadSeg(@_DataInit);        {note that _DataInit must not be in Main!}
  808.     MaxApplZone;                {expand the heap so code segments load at the top}
  809.  
  810.     InitSignals;
  811.     Initialize;                    {initialize the program}
  812.     UnloadSeg(@Initialize);        {note that Initialize must not be in Main!}
  813.  
  814.     EventLoop;                    {call the main event loop}
  815. END.
  816.